﻿
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace UGUIFW
{
    public class UIManager : MonoBehaviour
    {
        /* 字段 */
        private static UIManager instance = null;
        //缓存所有UI窗体
        private Dictionary<EUIName, BaseUI> allUI;
        //所有加载普通窗体
        private List<BaseUI> allNormalUI;
        //所有加载固定窗体
        private List<BaseUI> allFixedUI;
        //所有加载弹出窗体
        private List<BaseUI> allPopupUI;
        //弹窗管理的栈结构
        private Stack<BaseUI> popupUIStack;

        //UI根节点
        public Transform UIRoot = null;
        //全屏幕显示的节点
        public Transform normalUIRoot = null;
        //固定显示的节点
        public Transform fixedUIRoot = null;
        //弹出节点
        public Transform popUpUIRoot = null;
        //脚本根节点
        public Transform scriptRoot = null;
        //UI摄像机
        public Camera UICamera = null;

        public static UIManager GetInstance()
        {
            if (instance == null)
            {
                instance = new GameObject("UIManager").AddComponent<UIManager>();
            }
            return instance;
        }

        //初始化核心数据，加载“UI窗体路径”到集合中。
        public void Awake()
        {
            allUI = new Dictionary<EUIName, BaseUI>();
            allNormalUI = new List<BaseUI>();
            allFixedUI = new List<BaseUI>();
            allPopupUI = new List<BaseUI>();
            popupUIStack = new Stack<BaseUI>();
            //初始化加载（根UI窗体）Canvas预设
            GameObject go = Resources.Load<GameObject>("UIPrefabs/UIRoot");
            UIRoot = Instantiate(go).transform;
            Debug.Assert(UIRoot != null, "UIRoot == null");
            //得到全屏节点、固定节点、弹出节点
            normalUIRoot = UIRoot.Find("Normal");
            fixedUIRoot = UIRoot.Find("Fixed");
            popUpUIRoot = UIRoot.Find("Popup");
            scriptRoot = UIRoot.Find("ScriptRoot");
            UICamera = UIRoot.Find("UICamera").GetComponent<Camera>();
            //把本脚本作为UI脚本节点的根节点。
            this.gameObject.transform.SetParent(scriptRoot, false);
            //在场景转换的时候，不允许销毁
            DontDestroyOnLoad(UIRoot);

            SceneManager.sceneLoaded += OnSceneLoaded;
        }

        /// <summary>
        /// 调用当前显示的UI的生命周期
        /// </summary>
        private void Update()
        {
            foreach (var item in allUI)
            {
                if (item.Value.gameObject.activeSelf)
                {
                    item.Value.Update();
                }
            }
        }

        /// <summary>
        /// 显示UI窗体
        /// 功能：
        /// 1: 根据UI窗体的名称，加载到“所有UI窗体”缓存集合中
        /// 2: 根据不同的UI窗体的类型，分别作不同的加载处理
        /// </summary>
        /// <param name="uiName"></param>
        /// <param name="isAsync">是否异步加载</param>
        /// <param name="action">外部方法</param>
        /// <param name="data">外部参数</param>
	    public void ShowUI(EUIName uiName, bool isAsync = false, Action action = null, object data = null)
        {
            BaseUI baseUI = null;
            if (!allUI.ContainsKey(uiName))
            {
                baseUI = UIData.GetUIData(uiName);
            }
            else
            {
                baseUI = allUI[uiName];
            }
            //参数检查
            if (baseUI == null || baseUI.Info == null || string.IsNullOrEmpty(baseUI.Info.PrefabPath))
            {
                Debug.Log("ShowUI() 的时候数据缺失,请检查!");
                return;
            }

            //设置传进来的外部方法和外部参数
            baseUI.ExternalAction = action;
            baseUI.ExternalData = data;
            //如果没有缓存,根据UI窗体的名称，加载到“所有UI窗体”缓存集合中
            if (!allUI.ContainsKey(uiName))
            {
                //加载指定名称的“UI窗体”
                if (!isAsync)
                {
                    LoadUI(baseUI);
                }
                // 异步加载指定名称的“UI窗体”
                else
                {
                    StartCoroutine(LoadAsync(baseUI, Show));
                }
            }
            //有缓存,直接处理显示关系即可
            else
            {
                SetUIShowMode(baseUI);
            }

            //如果有附加UI,则显示附加UI
            if (baseUI.Info.AttachUIName != EUIName.None)
            {
                ShowUI(baseUI.Info.AttachUIName);
            }
        }

        /// <summary>
        /// 关闭（返回上一个）窗体
        /// </summary>
        /// <param name="baseUI"></param>
        public void CloseUI(BaseUI baseUI)
        {
            //参数检查
            if (baseUI == null || baseUI.Info == null || string.IsNullOrEmpty(baseUI.Info.PrefabPath))
            {
                Debug.Log("CloseUI() 的时候数据缺失,请检查!");
                return;
            }
            if (!allUI.ContainsKey(baseUI.Info.uiName))
            {
                Debug.LogError("正在尝试关闭一个没有实例化出来的UI, path = " + baseUI.Info.PrefabPath);
                return;
            }
            //根据窗体不同的显示类型，分别作不同的关闭处理
            switch (baseUI.Info.UI_BaseType)
            {
                case EUIBaseType.Normal:
                    //普通窗体的关闭
                    ExitNormalUI();
                    break;
                case EUIBaseType.Popup:
                    //Popup窗体的关闭
                    ExitPopUI();
                    break;
                case EUIBaseType.Fixed:
                    break;
                default:
                    break;
            }
        }

        #region 私有方法
        /// <summary>
        /// 加载指定名称的“UI窗体”
        /// 功能：
        ///    1：根据“UI窗体名称”，加载预设克隆体。
        ///    2：根据不同预设克隆体中带的脚本中不同的“位置信息”，加载到“根窗体”下不同的节点。
        ///    3：隐藏刚创建的UI克隆体。
        ///    4：把克隆体，加入到“所有UI窗体”（缓存）集合中。
        /// </summary>
        /// <param name="baseUI"></param>
	    private void LoadUI(BaseUI baseUI)
        {
            GameObject go = Resources.Load<GameObject>(baseUI.Info.PrefabPath);
            if (go == null)
            {
                Debug.LogError("实例化的prefab为null,请检查. path = " + baseUI.Info.PrefabPath);
                return;
            }
            Show(baseUI, Instantiate(go));
        }

        /// <summary>
        /// 异步加载指定名称的“UI窗体”
        /// 功能：
        ///    1：根据“UI窗体名称”，加载预设克隆体。
        ///    2：根据不同预设克隆体中带的脚本中不同的“位置信息”，加载到“根窗体”下不同的节点。
        ///    3：隐藏刚创建的UI克隆体。
        ///    4：把克隆体，加入到“所有UI窗体”（缓存）集合中。
        /// </summary>
        /// <param name="baseUI"></param>
        /// <param name="callbak"></param>
        IEnumerator LoadAsync(BaseUI baseUI, Action<BaseUI, GameObject> callbak)
        {
            ResourceRequest request = Resources.LoadAsync(baseUI.Info.PrefabPath);
            while (!request.isDone)
            {
                yield return null;
            }
            if (request.asset == null)
            {
                Debug.LogError("加载的prefab为null,请检查. path = " + baseUI.Info.PrefabPath);
            }
            GameObject go = Instantiate(request.asset) as GameObject;
            if (go == null)
            {
                Debug.LogError("实例化的prefab为null,请检查. path = " + baseUI.Info.PrefabPath);
                yield break;
            }
            callbak(baseUI, go);
        }

        /// <summary>
        /// 把加载好的UI归类设置父节点,并放入集合中
        /// </summary>
        /// <param name="baseUI"></param>
        /// <param name="go"></param>
        void Show(BaseUI baseUI, GameObject go)
        {
            //设置“UI克隆体”的父节点（根据克隆体中带的脚本中不同的“位置信息”）
            baseUI.SetGameObject(go);
            switch (baseUI.Info.UI_BaseType)
            {
                case EUIBaseType.Normal:                 //普通窗体节点
                    baseUI.transform.SetParent(normalUIRoot, false);
                    break;
                case EUIBaseType.Fixed:                  //固定窗体节点
                    baseUI.transform.SetParent(fixedUIRoot, false);
                    break;
                case EUIBaseType.Popup:                  //弹出窗体节点
                    baseUI.transform.SetParent(popUpUIRoot, false);
                    break;
                default:
                    break;
            }
            go.SetActive(false);
            //把克隆体，加入到“所有UI窗体”（缓存）集合中。
            allUI.Add(baseUI.Info.uiName, baseUI);
            SetUIShowMode(baseUI);
        }

        /// <summary>
        /// 根据UI的配置,处理UI的显示
        /// </summary>
        /// <param name="baseUI"></param>
        void SetUIShowMode(BaseUI baseUI)
        {
            switch (baseUI.Info.UI_BaseType)
            {
                case EUIBaseType.Normal:
                    EnterNormalUI(baseUI);
                    break;
                case EUIBaseType.Fixed:
                    EnterFixedUI(baseUI);
                    break;
                case EUIBaseType.Popup:
                    EnterPopUI(baseUI);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 常规窗口的显示(一般是全屏显示的UI,可以附加 子UI)
        /// </summary>
        /// <param name="baseUI"></param>
        private void EnterNormalUI(BaseUI baseUI)
        {
            //隐藏上一个常规UI窗体
            if (allNormalUI.Count > 0)
            {
                BaseUI lastUI = allNormalUI[allNormalUI.Count - 1];
                lastUI.Hide();
                if (baseUI.Info.AttachUIName != lastUI.Info.AttachUIName)
                {
                    BaseUI attachUI = GetAttachUI(lastUI);
                    if (attachUI != null)
                    {
                        attachUI.Hide();
                    }
                }
            }
            ReDisplayOrDisplay(allNormalUI, baseUI);
        }

        /// <summary>
        /// 固定窗口,一般作为常规UI的 子UI
        /// </summary>
        /// <param name="baseUI"></param>
	    private void EnterFixedUI(BaseUI baseUI)
        {
            ReDisplayOrDisplay(allFixedUI, baseUI);
        }

        /// <summary>
        /// popup窗口的显示
        /// </summary>
        /// <param name="baseUI"></param>
        private void EnterPopUI(BaseUI baseUI)
        {
            //上个显示中的窗体做“冻结”处理。
            if (popupUIStack.Count > 0)
            {
                BaseUI topUI = popupUIStack.Peek();
                //栈顶元素作冻结处理
                topUI.Freeze();
            }
            else
            {
                if (allNormalUI.Count > 0)
                {
                    allNormalUI[allNormalUI.Count - 1].Freeze();
                }
            }

            popupUIStack.Push(baseUI);

            ReDisplayOrDisplay(allPopupUI, baseUI);
        }
        /// <summary>
        /// 判断并调用UI的 Display , ReDisplay方法
        /// </summary>
        /// <param name="uIs"></param>
        /// <param name="baseUI"></param>
        void ReDisplayOrDisplay(List<BaseUI> uIs, BaseUI baseUI)
        {
            if (!uIs.Contains(baseUI))
            {
                baseUI.Display();
                uIs.Add(baseUI);
            }
            else
            {
                //重新显示,并移动到列表末尾
                if (baseUI.gameObject.activeSelf == false)
                {
                    baseUI.Redisplay();
                }
                uIs.Remove(baseUI);
                uIs.Add(baseUI);
            }
        }

        /// <summary>
        /// Popup窗体的关闭
        /// </summary>
        private void ExitPopUI()
        {
            //隐藏当前窗体
            if (allPopupUI.Count > 0)
            {
                BaseUI baseUI = popupUIStack.Pop();
                baseUI.Hide();
            }
            //解冻上层的窗体
            if (popupUIStack.Count > 0)
            {
                BaseUI baseUI = popupUIStack.Peek();
                baseUI.UnFreeze();
            }
            else
            {
                if (allNormalUI.Count > 0)
                {
                    allNormalUI[allNormalUI.Count - 1].UnFreeze();
                }
            }
        }

        /// <summary>
        /// 常规窗体的关闭，显示上一个窗体
        /// </summary>
        private void ExitNormalUI()
        {
            //指定窗体隐藏,且移动到列表前端
            if (allNormalUI.Count > 0)
            {
                BaseUI baseUI = allNormalUI[allNormalUI.Count - 1];
                allNormalUI.Remove(baseUI);
                baseUI.Hide();
                allNormalUI.Insert(0, baseUI);
                //显示上一个窗体
                BaseUI displayUI = allNormalUI[allNormalUI.Count - 1];
                displayUI.Redisplay();
                //对附加UI进行处理
                if (baseUI.Info.AttachUIName != displayUI.Info.AttachUIName)
                {
                    BaseUI baseUIAttach = GetAttachUI(baseUI);
                    BaseUI displayUIAttach = GetAttachUI(displayUI);

                    if (baseUIAttach != null)
                    {
                        baseUIAttach.Hide();
                    }
                    if (displayUIAttach != null)
                    {
                        displayUIAttach.Redisplay();
                    }
                }
            }
        }

        /// <summary>
        /// 取当前UI的附属UI
        /// </summary>
        /// <param name="baseUI"></param>
        /// <returns>null = 没有附属UI</returns>
        BaseUI GetAttachUI(BaseUI baseUI)
        {
            if (baseUI.Info.AttachUIName != EUIName.None)
            {
                for (int i = 0; i < allFixedUI.Count; i++)
                {
                    if (allFixedUI[i].Info.uiName == baseUI.Info.AttachUIName)
                    {
                        return allFixedUI[i];
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 切换场景的时候清理UI数据
        /// </summary>
        /// <param name="scene"></param>
        /// <param name="mode"></param>
        private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            Debug.Log("加载" + scene.name);
            allNormalUI.Clear();
            allPopupUI.Clear();
            foreach (var item in allUI)
            {
                item.Value.DestroyUI();
            }
            allUI.Clear();
        }
        #endregion
    }
}